using Kickball.Step1;
using Kickball.Step2;
using Kickball.Step3;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;

namespace Kickball.Step4
{
    [UpdateBefore(typeof(TransformSystemGroup))]
    public partial struct BallMovementSystem_Job : ISystem
    {
        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            state.RequireForUpdate<Execute.BallMovement_Job>();
            state.RequireForUpdate<Config>();
        }

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            var config = SystemAPI.GetSingleton<Config>();

            float dt = SystemAPI.Time.DeltaTime;

            float decayFactor = config.BallVelocityDecay * dt; //速度衰减因子
            float ballRadius = 0.5f;
            float minDist = config.ObstacleRadius + ballRadius;
            float minDistSq = minDist * minDist;

            var obstacleQuery = SystemAPI.QueryBuilder().WithAll<LocalTransform, Obstacle>().Build();

            var ballMovementJob = new BallMovementJob()
            {
                Obstacles = obstacleQuery.ToComponentDataArray<LocalTransform>(state.WorldUpdateAllocator),
                DecayFactor = decayFactor,
                DeltaTime = dt,
                MinDistSq = minDistSq,
            };
            ballMovementJob.ScheduleParallel();
        }
    }

    [BurstCompile]
    [WithAll(typeof(Ball))]
    [WithDisabled(typeof(Carry))]
    public partial struct BallMovementJob : IJobEntity
    {
        [ReadOnly] public NativeArray<LocalTransform> Obstacles;
        public float DecayFactor;
        public float DeltaTime;
        public float MinDistSq;

        public void Execute(ref LocalTransform transform, ref Velocity velocity)
        {
            if (velocity.Value.Equals(float2.zero)) return; //静止的球

            float magnitude = math.length(velocity.Value); //速度大小（标量）
            float3 pos = transform.Position +
                         new float3(velocity.Value.x, 0, velocity.Value.y) * DeltaTime;

            foreach (var obstacleTrans in Obstacles)
            {
                if (math.distancesq(obstacleTrans.Position, pos) < MinDistSq) //球碰撞到了障碍物
                {
                    //计算反弹速度和位置
                    pos = DeflectBall(transform.Position, obstacleTrans.Position, ref velocity,
                        magnitude, DeltaTime);
                    break;
                }
            }

            //球位置
            transform.Position = pos;

            //速度衰减
            float newMagnitude = math.max(magnitude - DecayFactor, 0);
            velocity.Value = math.normalizesafe(velocity.Value) * newMagnitude;
        }

        /// <summary>
        /// 偏转球
        /// </summary>
        /// <param name="ballPos">球位置</param>
        /// <param name="obstaclePos">障碍位置</param>
        /// <param name="velocity">速度向量</param>
        /// <param name="magnitude">速度标量</param>
        /// <param name="dt">时间步长</param>
        /// <returns>偏转后球的位置</returns>
        float3 DeflectBall(float3 ballPos, float3 obstaclePos, ref Velocity velocity, float magnitude, float dt)
        {
            float2 obstacleToBallVector = math.normalize((ballPos - obstaclePos).xz); //障碍指向球的向量（反射的法向量）
            velocity.Value =
                math.reflect(math.normalize(velocity.Value), obstacleToBallVector) * magnitude;
            return ballPos + new float3(velocity.Value.x, 0, velocity.Value.y) * dt;
        }
    }
}